<template>
  <div class="treeSelect">
    <el-select v-model="valueTitle" :clearable="clearable" :multiple="multiple" :collapse-tags="multiple"
      :popper-append-to-body="false" :filterable="filter" ref="select" @visible-change="handleScroll"
      @remove-tag="removeTag" @clear="clearHandle" :filter-method="filterMethod" :placeholder="placeholder">
      <el-option :value="valueTitle">
        <el-tree id="tree-option" ref="selectTree" :accordion="accordion" :data="data" :props="props"
          :node-key="nodeKey" :default-expanded-keys="defaultExpandedKey" :default-checked-keys="defaultExpandedKey"
          :filter-node-method="filterNode" @node-click="nodeClick" @check="checkBox" :show-checkbox="multiple"
          :expand-on-click-node="multiple" :check-strictly="checkStrictly">
          <div class="el-select-tree__item" slot-scope="{ data }" :class="treeItemClass(data)">
            {{ data[props.label] }}
          </div>
        </el-tree>
      </el-option>
    </el-select>
  </div>
</template>

<script>
export default {
  name: 'el-tree-select',
  model: {
    prop: 'value',
    event: 'change'
  },
  props: {
    /* 配置项 */
    props: {
      type: Object,
      default: () => {
        return {
          value: 'id', // ID字段名
          label: 'title', // 显示名称
          children: 'children', // 子级字段名
          parentValue: 'parentId'
        }
      }
    },
    /* 选项列表数据(树形结构的对象数组) */
    data: {
      type: Array,
      default: () => {
        return []
      }
    },
    /* 初始值 */
    value: {
      type: [Number, String, Array],
      default: ''
    },
    /* 可清空选项 */
    clearable: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    /* 是否可以多选，比较麻烦，以后再做 */
    multiple: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    /* 单选是否可以选择父节点、多选父子不互相关联 */
    checkStrictly: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    /* 可收索选项 */
    filter: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    /* 自动收起 */
    accordion: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    placeholder: {
      type: String,
      default: () => {
        return '请选择'
      }
    }
  },
  computed: {
    nodeKey() {
      return this.props.value || 'id'
    },
    defaultExpandedKey() {
      return Array.isArray(this.value)
        ? this.value
        : this.value || this.value === 0
          ? [this.value]
          : []
    }
  },
  data() {
    return {
      valueTitle: '',
      parent: [],
      children: [],
      newTreeList: []
    }
  },
  created() {
    if (this.multiple && !Array.isArray(this.value)) {
      throw new Error(
        '[el-select-tree] props `value` must be Array if use multiple!'
      )
    }
  },
  mounted() {
    this.initHandle()
  },
  methods: {
    filterMethod(node) {
      this.$refs.selectTree.filter(node)
    },
    valueChange(value, node) {
      this.$emit('getValue', value, node)
    },
    // 初始化值
    initHandle() {
      const treeSelect = this.$refs.selectTree
      this.$nextTick(() => {
        if (this.multiple) {
          treeSelect.setCheckedKeys(this.value)
          this.setMultipleSelectedLabel()
        } else {
          const treeSelectNode = treeSelect.getNode(this.value)
          this.valueTitle = treeSelectNode
            ? treeSelectNode.data[this.props.label]
            : ''
        }
      })
      this.handleScroll()
    },
    // 初始化滚动条
    handleScroll() {
      this.$nextTick(() => {
        let scrollWrap = document.querySelectorAll(
          '.treeSelect .el-scrollbar .el-select-dropdown__wrap .el-select-dropdown__item'
        )[0]
        scrollWrap.scrollTop = 0
      })
    },
    // 切换选项
    nodeClick(data, node, component) {
      const children = data[this.props.children]
      const value = data[this.props.value]
      if (children && children.length && !this.checkStrictly) {
        component.handleExpandIconClick()
      } else if (!this.multiple) {
        if (value !== this.value) {
          this.valueChange(value, data)
          this.valueTitle = data[this.props.label]
        }
        this.$refs.select.blur()
      }
    },
    // 多选模式下移除tag时触发
    removeTag(data) {
      const getCheckedKeys = this.$refs.selectTree.getCheckedKeys()
      const removeNodeKeys = this.newTreeList.filter(
        (o) => o[this.props.label] == data
      )[0][this.props.value]
      const newCheckNodeKeys = getCheckedKeys.filter((o) => o != removeNodeKeys)

      this.$refs.selectTree.setCheckedKeys([])
      this.$refs.selectTree.setCheckedKeys(newCheckNodeKeys)
      this.valueChange(newCheckNodeKeys)
    },
    // 清除选中
    clearHandle() {
      if (this.multiple) {
        this.valueChange([])
        this.$nextTick(() => {
          this.$refs.selectTree.setCheckedKeys([])
        })
        this.valueTitle = []
      } else {
        this.valueChange('')
        this.valueTitle = ''
      }
      this.clearSelected()
    },
    /* 清空选中样式 */
    clearSelected() {
      let allNode = document.querySelectorAll('#tree-option .el-tree-node')
      allNode.forEach((element) => element.classList.remove('is-current'))
    },
    filterNode(value, data) {
      if (!value) return true
      return data[this.props.label].indexOf(value) !== -1
    },
    /* 选中的样式 */
    treeItemClass(data) {
      return {
        'is-selected': this.multiple
          ? false
          : data[this.props.value] === this.value
      }
    },
    // 扁平化数据，方便使用
    getNewArray(source) {
      source.forEach((el) => {
        this.newTreeList.push(el)
        el[this.props.children] && el[this.props.children].length > 0
          ? this.getNewArray(el[this.props.children])
          : ''
      })
    },
    // 找到父级数据
    getParent(obj) {
      if (!!obj && obj[this.props.parentValue]) {
        this.parent.push(obj[this.props.parentValue])
        const parent = this.newTreeList.filter(
          (o) => o[this.props.value] === obj[this.props.parentValue]
        )[0]
        this.getParent(parent)
      }
      return this.parent
    },
    // 找打子级数据
    getChildren(obj) {
      if (obj[this.props.children]) {
        obj[this.props.children].map((o) => this.getChildren(o))
      }
      this.children.push(obj[this.props.value])
      return this.children
    },
    checkBox(obj, data) {
      this.children = []
      this.parent = []
      const getCheckedKeys = this.$refs.selectTree.getCheckedKeys()
      const children = this.getChildren(obj).filter(
        (o) => o != obj[this.props.value]
      )
      if (data.checkedKeys.includes(obj[this.props.value])) {
        const parent = Array.from(new Set(this.getParent(obj)))
        this.$refs.selectTree.setCheckedKeys(
          getCheckedKeys.concat(children).concat(parent)
        )
      } else {
        let c = []
        for (let e of getCheckedKeys) {
          if (!children.includes(e)) {
            c.push(e)
          }
        }
        this.$refs.selectTree.setCheckedKeys([])
        this.$refs.selectTree.setCheckedKeys(c)
      }
      this.$nextTick(() => {
        this.valueChange(
          this.$refs.selectTree.getCheckedKeys(),
          this.$refs.selectTree.getCheckedNodes()
        )
        this.setMultipleSelectedLabel()
      })
    },
    setMultipleSelectedLabel() {
      const selectTree = this.$refs.selectTree
      const selectedNodes = selectTree.getCheckedNodes()
      this.valueTitle = selectedNodes.map((item) => item[this.props.label])
    }
  },
  watch: {
    value() {
      this.initHandle()
    },
    data(val, oldval) {
      this.initHandle()
      if (val.length) this.getNewArray(JSON.parse(JSON.stringify(val)))
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="less" scoped>
:deep(.el-select) {
  width: 100%;

  :deep(.el-select-dropdown) {
    max-height: 280px;
    margin-top: 10px;
    left: 0 !important;
    position: absolute !important;
    top: 40px !important;

    :deep(.el-scrollbar) {
      height: 100%;
      overflow: hidden;

      :deep(.el-scrollbar__wrap) {
        overflow: initial;
        margin: 0 !important;

        :deep(.el-select-dropdown__item) {
          height: auto;
          max-height: 200px;
          padding: 0;
          overflow: hidden;
          overflow-y: auto;

          &.selected {
            font-weight: normal;
          }

          :deep(.el-tree-node__content) {
            height: auto;
            padding: 0 20px;
          }

          .is-selected {
            color: #409eff;
            font-weight: 700;
          }

          .is-current :deep(.el-tree-node__children) :deep(.el-tree-node__label) {
            color: #606266;
            font-weight: normal;
          }
        }
      }
    }
  }
}
</style>
